/*
 * Copyright 2003-2005 (c) Live Media Pty Ltd. <argot@einet.com.au> 
 *
 * This software is licensed under the Argot Public License 
 * which may be found in the file LICENSE distributed 
 * with this software.
 *
 * More information about this license can be found at
 * http://www.einet.com.au/License
 * 
 * The Developer of this software is Live Media Pty Ltd,
 * PO Box 4591, Melbourne 3001, Australia.  The license is subject 
 * to the law of Victoria, Australia, and subject to exclusive 
 * jurisdiction of the Victorian courts.
 */
 
#include "TypeMeta.h"

#include "MetaMap.h"

#include "../common/BigEndianUnsignedShort.h"

type_name meta_map_name = { "meta.map" };

int meta_map_get_abstract_type( meta_map * mm )
{
	return mm->abstract;
}

int meta_map_get_concrete_type( meta_map * mm )
{
	return mm->concrete;
}

void meta_map_set_abstract_type( meta_map * mm, int type )
{
	mm->abstract = type;
}

void meta_map_set_concrete_type( meta_map * mm, int type )
{
	mm->concrete = type;
}

static int meta_map_bind( type_element * element, type_definition * definition )
{
	meta_map * mm = (meta_map *) element;
	type_library * library = definition->library;
	int id = definition->id;
	int rc;
	
	rc = type_object_bind( &element->object, definition->library, &meta_map_name );
	if ( rc != TYPE_OK ) return rc;

	rc = type_element_bind( element, definition );
	if ( rc != TYPE_OK ) return rc;
	
	
	if ( type_library_get_id_type_state( library, mm->abstract ) == TYPE_NOT_DEFINED )
	{
		return TYPE_BIND_ERROR;
	}
	
	if ( type_library_get_id_type_state( library, mm->concrete ) == TYPE_NOT_DEFINED )
	{
		return TYPE_BIND_ERROR;
	}
	
	type_element * ma = type_library_get_structure( library, mm->abstract );
	type_name * name = type_object_name( &ma->object );
	if ( name->hash != meta_abstract_name.hash )
	{
		return TYPE_BIND_ERROR;
	}

	meta_abstract_add_map( (meta_abstract*)ma, mm->concrete, id );	
	
	return TYPE_OK;
}

void meta_map_init( meta_map * mm, int abstract, int concrete )
{
	type_element_init( &mm->expression.element, &meta_map_bind );
	mm->abstract = abstract;
	mm->concrete = concrete;
}

void * meta_map_construct( type_list * objects, type_map * map )
{
	reference_type_map * map_core = (reference_type_map *) map;
	type_map * ref_map = reference_type_map_get_reference( map_core );
	type_item * item = type_list_head( objects );
	int abstract = type_item_get_u16( item );
	item = type_item_next( item );
	int concrete = type_item_get_u16( item );

	/* check values are valid and  */
	if ( ! type_map_is_valid_id( ref_map, abstract ) )
	{
		/* TODO set error. */
	}
	abstract = type_map_get_library_id( ref_map, abstract );
	
	if ( ! type_map_is_valid_id( ref_map, concrete ) )
	{
		/* TODO set error. */
	}
	concrete = type_map_get_library_id( ref_map, concrete );
	
	meta_map * mm = (meta_map *) type_malloc( sizeof( meta_map ) );
	meta_map_init( mm, abstract, concrete );
	return mm;
}

int meta_map_read( type_input_stream * in, type_element * element )
{
	return type_reader_auto( in, element, meta_map_construct );
}

int meta_map_write( type_output_stream * out, void * o, type_element * element )
{
	meta_map * mm = (meta_map *) o;
	reference_type_map * map_core = (reference_type_map *) type_output_type_map( out );
	type_map * map = reference_type_map_get_reference( map_core );
	int abstractid = type_map_get_mapped_id( map, mm->abstract );
	type_output_write_u16( out, abstractid );
	int concreteid = type_map_get_mapped_id( map, mm->concrete );
	type_output_write_u16( out, concreteid );
	return TYPE_OK;	
}

int meta_map_do_read( type_input_stream * in, type_element * element )
{
	/* This shouldn't happen.
	   Someone reads an abstract type.  Which checks its local list and reads directly.
	 */
	
	meta_map * mm = (meta_map *) element;
	type_input_read_object( in, type_map_get_mapped_id( type_input_type_map( in ), mm->concrete ) );
	return TYPE_OK;
}

int meta_map_do_write( type_output_stream * out, void * o, type_element * element )
{
	/* This shouldn't happen. */
	meta_map * mm = (meta_map *) element;
	type_map * map = (type_map *) type_output_type_map( out );
	
	/* FIXME
	  type_output_write_u16( out, type_map_get_mapped_id( map, type_element_get_type_id( element ) ) );
	 */
	
	type_output_write_object( out, type_map_get_mapped_id( map, mm->concrete ), o );
	return TYPE_OK;	
}
